/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { XTools } = require("../../engine/XTools");
const { PackageTool } = require("../ros_msg_utils/PackageTool");
const { RosProto } = require("../ros_msg_utils/RosProto");

class ProjectConfig {
    static VERSION = 1;
    static WORKSPACE_PATH = "";// 如ubuntu上ros1的工作空间路径：/home/cc/ros_workspace/ros1
    static PROJECT_NAME = "";// 用户创建的项目名称
    static CONFIG_FILE_NAME = "mestro.config";// 配置文件名
    constructor() {
        this.runtime = {
            "node": {},
            "topic": {},
            "service": {},
        };
        this.custom = {
            "node": {},
            "message": {},
        };
    }
    static getInstance() {
        if (!ProjectConfig.instance) {
            ProjectConfig.instance = new ProjectConfig();
        }
        return ProjectConfig.instance;
    }
    setCustomAttr() {
        const { CustomManager } = require("./1/CustomManager");
        let nodes = CustomManager.getInstance().nodes;
        let messages = CustomManager.getInstance().messages;
        for (let node of nodes) {
            let changed = false;
            if (node.name in this.custom.node) {
                if (this.custom.node[node.name].name != node.name ||
                    this.custom.node[node.name].code != node.code ||
                    this.custom.node[node.name].x != node.x ||
                    this.custom.node[node.name].y != node.y ||
                    this.custom.node[node.name].token != node.token) {
                    changed = true;
                }
            } else changed = true;
            if (changed) {
                this.custom.node[node.name] = {
                    "name": node.name,
                    "code": node.code,
                    "x": node.x,
                    "y": node.y,
                    "token": node.token,
                }
                this.needToSave = true;
            }
        }
        for (let name in this.custom.node) {
            let has = false;
            for (let node of nodes) {
                if (name == node.name) {
                    has = true;
                }
            }
            if (!has) {
                delete this.custom.node[name];
                this.needToSave = true;
            }
        }
        for (let message of messages) {
            let changed = false;
            if (message.name in this.custom.message) {
                if (this.custom.message[message.name].name != message.name ||
                    this.custom.message[message.name].x != message.x ||
                    this.custom.message[message.name].y != message.y ||
                    this.custom.message[message.name].token != message.token ||
                    message.param.length != this.custom.message[message.name].param.length) {
                    changed = true;
                } else {
                    for (let i = 0; i < message.param.length; i++) {
                        let s = JSON.stringify(message.param[i]);
                        if (this.custom.message[message.name].param.indexOf(s) == -1) {
                            changed = true;
                            break;
                        }
                    }
                }

            } else changed = true;
            if (changed) {
                this.custom.message[message.name] = {
                    "name": message.name,
                    "x": message.x,
                    "y": message.y,
                    "token": message.token,
                    "param": [],
                }
                for (let i = 0; i < message.param.length; i++) {
                    let s = JSON.stringify(message.param[i]);
                    this.custom.message[message.name].param.push(s);
                }
                this.needToSave = true;
            }
        }
        for (let name in this.custom.message) {
            let has = false;
            for (let message of messages) {
                if (name == message.name) {
                    has = true;
                }
            }
            if (!has) {
                delete this.custom.message[name];
                this.needToSave = true;
            }
        }
        if (this.needToSave) {
            this.delaySaveConfig();
        }
    }
    delaySaveConfig() {
        this.needToSave = true;
        setTimeout(() => {//保存频率1秒，如果连续多次更新，只保存最后一次
            if (this.needToSave) {
                this.saveConfig();
                this.needToSave = false;
            }
        }, 1000);
    }
    setRuntimeAttr(rb) {
        if (rb.name.startsWith("/rostopic_")) {//临时节点，每次都不一样，不参与保存
            return false;
        }
        if (!(rb.name in this.runtime[rb.type])) {
            this.runtime[rb.type][rb.name] = {
                "hide": false,
                "code": "",
                "code2": "",
                "x": -1,
                "y": -1,
            }
        }
        if (this.runtime[rb.type][rb.name].hide != rb.hide) {
            this.runtime[rb.type][rb.name].hide = rb.hide;
            XTools.RUNTIME_UPDATE_LINES = true;
        }
        if (this.runtime[rb.type][rb.name].code != rb.renderFunc) {
            this.runtime[rb.type][rb.name].code = rb.renderFunc;
            XTools.RUNTIME_UPDATE_LINES = true;
        }
        if (this.runtime[rb.type][rb.name].code2 != rb.publishFunc) {
            this.runtime[rb.type][rb.name].code2 = rb.publishFunc;
            XTools.RUNTIME_UPDATE_LINES = true;
        }
        if (this.runtime[rb.type][rb.name].x != rb.x || this.runtime[rb.type][rb.name].y != rb.y) {
            this.runtime[rb.type][rb.name].x = rb.x;
            this.runtime[rb.type][rb.name].y = rb.y;
            XTools.RUNTIME_UPDATE_LINES = true;
        }
        if (XTools.RUNTIME_UPDATE_LINES) {
            this.needToSave = true;
            setTimeout(() => {//保存频率1秒，如果连续多次更新，只保存最后一次
                if (this.needToSave) {
                    this.saveConfig();
                    this.needToSave = false;
                }
            }, 1000);
            return true;
        }
        return false;
    }
    getRuntimeAttr(type, name) {
        if (name in this.runtime[type]) {
            return this.runtime[type][name];
        }
        return false;
    }
    saveConfig() {
        let pt = new PackageTool(null, 1024 * 1024);//配置文件不超过1M
        pt.put_uint32(ProjectConfig.VERSION);

        for (let type of ["node", "topic", "service"]) {
            pt.put_uint32(Object.keys(this.runtime[type]).length);
            for (let name in this.runtime[type]) {
                pt.put_string(name);
                pt.put_bool(this.runtime[type][name].hide);
                pt.put_string(this.runtime[type][name].code);
                pt.put_string(this.runtime[type][name].code2);
                pt.put_int32(this.runtime[type][name].x);
                pt.put_int32(this.runtime[type][name].y);
            }
        }
        pt.put_uint32(Object.keys(this.custom.node).length);
        for (let name in this.custom.node) {
            pt.put_string(name);
            pt.put_string(this.custom.node[name].token);
            pt.put_string(this.custom.node[name].code);
            pt.put_int32(this.custom.node[name].x);
            pt.put_int32(this.custom.node[name].y);
        }
        pt.put_uint32(Object.keys(this.custom.message).length);
        for (let name in this.custom.message) {
            pt.put_string(name);
            pt.put_string(this.custom.message[name].token);
            pt.put_string_array(this.custom.message[name].param);
            pt.put_int32(this.custom.message[name].x);
            pt.put_int32(this.custom.message[name].y);
        }
        let configFilePath = ProjectConfig.WORKSPACE_PATH + "/" + ProjectConfig.PROJECT_NAME + "/" + ProjectConfig.CONFIG_FILE_NAME;
        RosProto.putFile(configFilePath, pt.offset[0], pt.buffer.subarray(0, pt.offset[0]));
    }
    loadConfig(cb) {
        let configFilePath = ProjectConfig.WORKSPACE_PATH + "/" + ProjectConfig.PROJECT_NAME + "/" + ProjectConfig.CONFIG_FILE_NAME;
        RosProto.getFile(configFilePath, (data) => {
            if (data) {
                this.parse(data);
            } else {
                this.saveConfig();
            }
            cb();
        })
    }
    parse(data) {
        this.runtime = {
            "node": {},
            "topic": {},
            "service": {},
        }
        let pt = new PackageTool(data);
        let version = pt.get_uint32();
        if (version != ProjectConfig.VERSION) {
            console.error("config version not match");
            return;
        }
        for (let type of ["node", "topic", "service"]) {
            let count = pt.get_uint32();
            for (let i = 0; i < count; i++) {
                let name = pt.get_string();
                this.runtime[type][name] = {
                    "hide": pt.get_bool(),
                    "code": pt.get_string(),
                    "code2": pt.get_string(),
                    // "code2": "",
                    "x": pt.get_int32(),
                    "y": pt.get_int32(),
                }
            }
        }
        let count = pt.get_uint32();
        this.custom.node = {};
        for (let i = 0; i < count; i++) {
            let name = pt.get_string();
            this.custom.node[name] = {
                "name": name,
                "token": pt.get_string(),
                "code": pt.get_string(),
                "x": pt.get_int32(),
                "y": pt.get_int32(),
            }
        }
        count = pt.get_uint32();
        this.custom.message = {};
        for (let i = 0; i < count; i++) {
            let name = pt.get_string();
            this.custom.message[name] = {
                "name": name,
                "token": pt.get_string(),
                "param": pt.get_string_array(),
                "x": pt.get_int32(),
                "y": pt.get_int32(),
            }
        }
        const { CustomManager } = require("./1/CustomManager");
        CustomManager.getInstance().doload(this.custom.node, this.custom.message);
    }
}

module.exports = {
    ProjectConfig
}